package com.redstar.HappyRefresh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.redstar.HappyRefresh.authority.AuthorityUtils;
import com.redstar.HappyRefresh.mapper.PermissionMapper;
import com.redstar.HappyRefresh.pojo.*;
import com.redstar.HappyRefresh.service.PermissionService;
import com.redstar.HappyRefresh.service.RolePermissionService;
import com.redstar.HappyRefresh.service.UserRoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService { // 权限服务

    @Resource
    private UserRoleService userRoleService; // 用户角色

    @Resource
    private RolePermissionService rolePermissionService; // 角色权限

    // Permission比较器，权限比较器
    private static class PermissionComparator implements Comparator<Permission> {
        @Override
        public int compare(Permission o1, Permission o2) {
            return o1.getSort() - o2.getSort();
        }
    }


    @Override
    public Map<String, Object> initMenu(Long uId) { // 初始化菜单，根据当前用户的权限获取菜单
        // 用户 => 用户角色 => 角色 => 角色权限 => 权限
        // 根据用户id查询对应的角色id
        List<Long> rIds = userRoleService.list(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, uId).select(UserRole::getRoleId))
                .stream().map(UserRole::getRoleId).collect(Collectors.toList());
        // 用户不存在角色，直接返回空集合
        if (ObjectUtils.isEmpty(rIds)) {
            return new HashMap<>();
        }
        // 根据角色查询对应的权限id
        List<Integer> pIds = rolePermissionService.list(new LambdaQueryWrapper<RolePermission>().in(RolePermission::getRoleId, rIds).select(RolePermission::getPermissionId))
                .stream().map(RolePermission::getPermissionId).collect(Collectors.toList());
        // 根据权限id查询权限
        List<Permission> permissionList = list(new LambdaQueryWrapper<Permission>().in(Permission::getId, pIds));
        List<Menu> menus = new ArrayList<>(); // 菜单
        List<Menu> parentMenu = new ArrayList<>(); // 根节点，包含业务管理和系统设置
        // 查出所有权限 => 转成对应的菜单对象
        permissionList.stream().sorted(new PermissionComparator())
            .forEach(permission -> {
                // Menu类的作用是包装权限，不同的权限，界面会有不同的内容
                Menu menu = new Menu();
                BeanUtils.copyProperties(permission, menu);
                menu.setTitle(permission.getName()); // 权限名称
                menus.add(menu);
            });
        // 菜单集合转成树形结构，储存在parentMenu集合中返回
        for (Menu menu : menus) {
            // pid指的是父节点，pid为0就是根节点
            if (menu.getPId().compareTo(0L) == 0) {
                parentMenu.add(menu);
            }
        }
        // 权限对应的操作，在递归构造权限树的同时完成对用户权限对应的操作的的封装
        Set<String> pathSet = new HashSet<>();
        for (Menu menu : parentMenu) {
           findChild(menu, menus, pathSet);
        }
        // 保存用户能够调用的操作
        AuthorityUtils.setAuthority(uId, pathSet);
        // 创建返回结果map
        Map<String, Object> data = new HashMap<>();
        MenuKey menuKey1 = new MenuKey();
        MenuKey menuKey2 = new MenuKey();
        menuKey1.setTitle("首页");
        menuKey1.setHref("page/welcome.html?t=1");
        menuKey2.setTitle("乐刷视频");
        menuKey2.setHref("/index.html");
        menuKey2.setImage("/images/favicon.ico");
        data.put("menuInfo", parentMenu);
        data.put("homeInfo", menuKey1);
        data.put("logoInfo", menuKey2);
        return data;
    }

    @Override
    public List<Permission> treeSelect() { // 只包含目录和菜单和的权限树
        List<Permission> permissionList = new ArrayList<>();
        // 查询除了按钮之外的所有权限，isMenu等于1即为按钮
        List<Permission> permissions = list(new LambdaQueryWrapper<Permission>().ne(Permission::getIsMenu, 1));
        // 找到根节点
        for (Permission permission : permissions) {
            if (permission.getPId().compareTo(0L) == 0) {
                permissionList.add(permission);
            }
        }
        // 根据根节点找到子节点
        for (Permission datum : permissionList) {
            findTreeSelectChildren(datum, permissions);
        }
        return permissionList;
    }

    @Override
    @Transactional
    public void removeMenu(Long id) { // 删除以id为根节点的所有权限
        // 封装id集合
        List<Long> ids = new ArrayList<>();
        findPermissionId(id, ids);
        ids.add(id); // 将根节点最后加入ids中，[1, 2, 3, 1, 2, 3, 0]
        removeByIds(ids);
        rolePermissionService.remove(new LambdaQueryWrapper<RolePermission>().in(RolePermission::getPermissionId, ids));
    }

    private void findPermissionId(Long id, List<Long> ids) { // 将以id为根节点的所有节点的id存储在ids中
        List<Permission> list = list(new LambdaQueryWrapper<Permission>().eq(Permission::getPId, id).select(Permission::getId));
        list.forEach(permission -> {
            ids.add(permission.getId());
            findPermissionId(permission.getId(), ids);
        });
        // ids中存储的节点关系：[1, 2, 3, 1, 2, 3]
    }

    private Permission findTreeSelectChildren(Permission datum, List<Permission> permissions) {  // 找到指定根节点的全部子节点，构造了一颗完整的树结构
        datum.setChildren(new ArrayList<>()); // 为权限创建子节点集合
        for (Permission permission : permissions) {
            if (datum.getId().compareTo(permission.getPId()) == 0) {
                // 递归调用
                datum.getChildren().add(findTreeSelectChildren(permission, permissions));
            }
        }
        return datum;
    }

    private Menu findChild(Menu menu, List<Menu> menus, Set<String> pathSet) { // 构造一棵权限树，set 权限集合
        menu.setChild(new ArrayList<>());
        for (Menu m : menus) {
            if (!ObjectUtils.isEmpty(m.getPath())) {
                pathSet.add(m.getPath()); // 将用户权限对应的操作进行封装
            }
            if (m.getIsMenu() != 1 && menu.getId().compareTo(m.getPId()) == 0) {
                // 递归调用
                menu.getChild().add(findChild(m, menus, pathSet));
            }
        }
        return menu;
    }
}
